﻿using System;
using System.Collections.Generic;

namespace SL5.Common.Comparers
{
	/// <summary>
	/// Static methods regarding projection comparer.
	/// </summary>
	public static class ProjectionComparer
	{
		#region Public Static Methods 
		/// <summary>
		/// Creates a projection comparer using the specified projection and value comparer.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <typeparam name="TValue">The type of the value.</typeparam>
		/// <param name="projection">The projection.</param>
		/// <param name="valueComparer">The value comparer.</param>
		/// <returns>ProjectionComparer.</returns>
		public static ProjectionComparer<T, TValue> Create<T, TValue>(Func<T, TValue> projection, IComparer<TValue> valueComparer = null)
		{
			return new ProjectionComparer<T, TValue>(projection, valueComparer);
		}
		#endregion Public Static Methods 
	}

	/// <summary>
	/// Presents the ability to make a comparer by projecting a value off the item being sorted and then returning a sort value based on that projected item.
	/// </summary>
	/// <typeparam name="T">The type of items being compared.</typeparam>
	/// <typeparam name="TValue">The type of the value.</typeparam>
	public sealed class ProjectionComparer<T, TValue> : Comparer<T>
	{
		#region Private Variables 
		private readonly Func<T, TValue> _projection;
		private readonly IComparer<TValue> _valueComparer;
		#endregion Private Variables 

		#region Public Properties 
		/// <summary>
		/// Gets the projection.
		/// </summary>
		public Func<T, TValue> Projection
		{
			get
			{
				return this._projection;
			}
		}
		#endregion Public Properties 

		#region Public Methods 
		/// <summary>
		/// When overridden in a derived class, performs a comparison of two objects of the same type and returns a value indicating whether one object is less than, equal to, or greater than the other.
		/// </summary>
		/// <param name="x">The first object to compare.</param>
		/// <param name="y">The second object to compare.</param>
		/// <returns>
		/// Value
		/// Condition
		/// Less than zero
		/// <paramref name="x"/> is less than <paramref name="y"/>.
		/// Zero
		/// <paramref name="x"/> equals <paramref name="y"/>.
		/// Greater than zero
		/// <paramref name="x"/> is greater than <paramref name="y"/>.
		/// </returns>
		/// <exception cref="T:System.ArgumentException">
		/// Type <typeparamref name="T"/> does not implement either the <see cref="T:System.IComparable`1"/> generic interface or the <see cref="T:System.IComparable"/> interface.
		///   </exception>
		public override int Compare(T x, T y)
		{
			return this._valueComparer.Compare(this._projection(x), this._projection(y));
		}
		#endregion Public Methods 

		#region Public Constructors 
		/// <summary>
		/// Initializes a new instance of the <see cref="ProjectionComparer&lt;T, TValue&gt;"/> class.
		/// </summary>
		/// <param name="projection">The projection.</param>
		/// <param name="valueComparer">The value comparer.</param>
		public ProjectionComparer(Func<T, TValue> projection, IComparer<TValue> valueComparer = null)
		{
			if (projection == null)
			{
				throw new ArgumentNullException("projection");
			}
			this._projection = projection;
			this._valueComparer = valueComparer ?? Comparer<TValue>.Default;
		}
		#endregion Public Constructors 
	}
}
